/**
  ******************************************************************************
  * @file    lin.c
  * @author  AE Team
  * @version 1.0.3
  * @date    2024-05-28
  * @brief
  *
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright (c) 2024 SIYIMicro.
  * All rights reserved.</center></h2>
  *
  *
  ******************************************************************************
  */


/******************************************************************************/
/* Include files                                                              */
/******************************************************************************/
#include "lin.h"


/******************************************************************************/
/* Local pre-processor symbols/macros ('#define')                             */
/******************************************************************************/

/******************************************************************************/
/* Global variable definitions (declared in header file with 'extern')        */
/******************************************************************************/
LIN_SlaveClassTypeDef  hLinSlave             = {0};
uint8_t                UserSendBuf[DATA_LEN] = {0};
uint8_t                UserRecvBuf[DATA_LEN] = {0};

/******************************************************************************/
/* Local type definitions ('typedef')                                         */
/******************************************************************************/

/******************************************************************************/
/* Local function prototypes ('static')                                       */
/******************************************************************************/



/*******************************************************************************
 * @brief  LIN 协议计算PID算法
 * @param  IdCode: PID的原始值ID
 *
 * @retval PID
 * @note
 */
uint8_t LIN_CalcPID(uint8_t IdCode)
{
    uint8_t parity, p0, p1;

    parity = IdCode;
    p0 = (HAL_BIT_GET(parity, 0) ^ HAL_BIT_GET(parity, 1) ^ HAL_BIT_GET(parity, 2) ^ HAL_BIT_GET(parity, 4)) << 6;
    p1 = (!(HAL_BIT_GET(parity, 1) ^ HAL_BIT_GET(parity, 3) ^ HAL_BIT_GET(parity, 4) ^ HAL_BIT_GET(parity, 5))) << 7;

    parity |= (p0 | p1);

    return parity;
}


/*******************************************************************************
 * @brief  LIN 协议计算校验和算法
 * @param  Buff  : 待发送的数据的指针
 * @param  DataLen: 待发送的数据的长度
 *
 * @retval PID
 * @note
 */
uint8_t LIN_CalcCheckSum(uint8_t *Buff, uint8_t DataLen)
{
    uint32_t CheckSum = 0;
    uint8_t i;

    for(i = 0; i < DataLen; i++)
    {
        CheckSum += Buff[i];
        if (CheckSum & 0xFF00)
        {
            CheckSum = (CheckSum & 0x00FF) + 1;
        }
    }
    return (uint8_t)CheckSum;
}



/*******************************************************************************
 * @brief  LIN 协议过程中延时，采用UART自带TIMER的通用定时模式实现延时n个bit
 * @param  TimeOutBit: 需要延时的n个bit的时长
 *
 * @retval None
 * @note
 */
void LIN_Delay_nBit(UART_HandleTypeDef *hUart, uint8_t TimeOutBit)
{
    uint32_t tmpTimeArr;
    uint32_t TmpBrri;
    if (TimeOutBit == 0)
    {
        return;
    }

    TmpBrri = hUart->Instance->BRRI;
    tmpTimeArr = ((TmpBrri << 4) + hUart->Instance->BRRF) * TimeOutBit;
    hUart->Instance->TIMARR = (tmpTimeArr >= 0x00FFFFFF) ? (0x00FFFFFF - 1) : tmpTimeArr; // 写入ARR
    REGBITS_MODIFY(hUart->Instance->CR2, UARTx_CR2_TIMCR_Msk, UART_TIMCR_NORMAL); // 启动UART的TIM 通用定时模式
    while (HAL_UART_GET_FLAG(hUart, UART_IT_FLAG_TIMOV) != UART_IT_FLAG_TIMOV) { ; } // 等待定时结束
    REGBITS_CLR(hUart->Instance->CR2, UARTx_CR2_TIMCR_Msk);           // 停止TIM
    HAL_UART_CLR_FLAG(hUart, UART_IT_FLAG_TIMOV);                     // 清除TIMOV标志
}


/*******************************************************************************
 * @brief  LIN 发送Break
 * @param  UARTx        : UART名称
 *                        SYM_UART1 / SYM_UART2
 * @param  SendBreakLen : 待发送的间隔段的长度
 * @param  SpaceBit     : 发送后等待字节间间隔长度
 * @retval None
 */
void LIN_SendBreak_Polling(UART_HandleTypeDef *hUart, uint16_t SendBreakLen, uint8_t SpaceBit)
{
    HAL_LIN_SendBreak(hUart->Instance, SendBreakLen);
    LIN_Delay_nBit(hUart, SpaceBit);
}


/*******************************************************************************
 * @brief  LIN 发送同步段
 * @param  UARTx        : UART名称
 *                        SYM_UART1 / SYM_UART2
 * @param  SendBreakLen : 待发送的间隔段的长度
 * @param  SpaceBit     : 发送后等待字节间间隔长度
 * @retval None
 */
void LIN_SendSync_Polling(UART_HandleTypeDef *hUart, uint8_t SpaceBit)
{
    HAL_UART_RXD_Disable(hUart->Instance);

    HAL_UART_SendByte(hUart->Instance, 0x55);
    LIN_Delay_nBit(hUart, SpaceBit);
}


/*******************************************************************************
 * @brief  通过查询方式发送LIN数据
 * @param  UARTx    : UART名称
 *                    SYM_UART1 / SYM_UART2
 * @param  pTxBuf   : 待发送的数据的指针
 * @param  TxCnt    : 待发送的数据的字节数量
 * @param  SpaceBit : 发送后等待字节间间隔长度
 * @retval None
 */
void LIN_SendData_Polling(UART_HandleTypeDef *hUart, uint8_t *pTxBuf, uint16_t TxCnt, uint8_t SpaceBit)
{
    HAL_UART_RXD_Disable(hUart->Instance);

    while (TxCnt--)
    {
        HAL_UART_SendByte(hUart->Instance, *pTxBuf++);
        LIN_Delay_nBit(hUart, SpaceBit);
    }
}


/*******************************************************************************
 * @brief  LIN 接收Break
 * @param  UARTx        : UART名称
 *                        SYM_UART1 / SYM_UART2
 * @param  TimeOut      : 等待接收数据的超时时间，以ms为单位。
 *                        0代表一直等待，没有超时功能
 * @retval HAL_OK       : 代表成功完成接收
 *         HAL_TIMEOUT  : 代表接收超时
 *         HAL_ERROR    : 代表接收出错
 */
HAL_StatusTypeDef LIN_RecvBreak_Polling(UART_HandleTypeDef *hUart, uint8_t TimeOut)
{
    uint32_t TickEnd  = HAL_GetTick() + TimeOut;

    HAL_UART_CLR_FLAG(hUart, UART_IT_FLAG_RXBRK);                     // 清间隔断接收完成标志
    while (HAL_UART_GET_FLAG(hUart, UART_IT_FLAG_RXBRK) != UART_IT_FLAG_RXBRK) // 等待间隔段接收完成
    {
        if (TimeOut && (HAL_GetTick() >= TickEnd))
        {
            return HAL_TIMEOUT;                                       // 超时退出
        }
    }
    HAL_UART_CLR_FLAG(hUart, UART_IT_FLAG_ALL);                       // 清所有标志

    return HAL_OK;
}


/*******************************************************************************
 * @brief  LIN 接收同步段
 * @param  UARTx        : UART名称
 *                        SYM_UART1 / SYM_UART2
 * @param  TimeOut      : 等待接收数据的超时时间，以ms为单位。
 *                        0代表一直等待，没有超时功能
 * @retval HAL_OK       : 代表成功完成接收
 *         HAL_TIMEOUT  : 代表接收超时
 *         HAL_ERROR    : 代表接收出错
 */
HAL_StatusTypeDef LIN_RecvSync_Polling(UART_HandleTypeDef *hUart, uint8_t TimeOut)
{
    uint32_t TickEnd  = HAL_GetTick() + TimeOut;

    hUart->Instance->TIMARR = 0xFFFFFF;
    REGBITS_MODIFY(hUart->Instance->CR2, UARTx_CR2_TIMCR_Msk, UART_TIMCR_AUTOBAUD2); // 启动UART的TIM 自动波特率检测模式2
    while (HAL_UART_GET_FLAG(hUart, UART_IT_FLAG_BAUD) != UART_IT_FLAG_BAUD) // 等待自动检测完成
    {
        if (TimeOut && (HAL_GetTick() >= TickEnd))
        {
            return HAL_TIMEOUT;                                       // 超时退出
        }
    }
    REGBITS_CLR(hUart->Instance->CR2, UARTx_CR2_TIMCR_Msk);           // 停止TIM
    HAL_UART_CLR_FLAG(hUart, UART_IT_FLAG_ALL);                       // 清所有标志
    hUart->Instance->BRRI = hUart->Instance->TIMCNT >> 7;
    hUart->Instance->BRRF = (hUart->Instance->TIMCNT >> 3) & 0x0F;

    return HAL_OK;
}


/*******************************************************************************
 * @brief  通过查询方式接收LIN数据
 * @param  UARTx      : UART名称
 *                      SYM_UART1 / SYM_UART2
 * @param  pRxBuf     : 待接收的数据的存放指针
 * @param  RxCnt      : 待接收的数据的字节数量
 * @param  TimeOut    : 等待接收数据的超时时间，以ms为单位。
 *                      0代表一直等待，没有超时功能
 * @retval HAL_OK     : 代表成功完成接收
 *         HAL_TIMEOUT: 代表接收超时
 *         HAL_ERROR  : 代表接收出错
 * @note   需要使能SYSTICK
 */
HAL_StatusTypeDef LIN_RecvData_Polling(UART_HandleTypeDef *hUart, uint8_t *pRxBuf, uint16_t RxCnt, uint8_t TimeOut)
{
    HAL_UART_RXD_Enable(hUart->Instance);

    uint32_t TickEnd = HAL_GetTick() + TimeOut;

    while (RxCnt)
    {
        uint8_t tmp8 =  hUart->Instance->ISR;

        if (tmp8 & UART_IT_FLAG_RC)
        {
            HAL_UART_CLR_FLAG(hUart, UART_IT_FLAG_RC);
            *pRxBuf = hUart->Instance->RDR;
            pRxBuf++;
            RxCnt--;
        }

        if (TimeOut && (HAL_GetTick() >= TickEnd))
        {
            return HAL_TIMEOUT;
        }
    }

    return HAL_OK;
}


__INLINE static void LIN_SlaveIT_PrepareRx(LIN_SlaveClassTypeDef *pLinSlave, uint8_t *pRxBuf, uint8_t RxCnt)
{
    pLinSlave->State = LIN_STATE_RXDATA;
    pLinSlave->RxTotalLen = RxCnt;
    pLinSlave->pRxBuff = pRxBuf;
}


__INLINE static void LIN_SlaveIT_PrepareTx(UART_HandleTypeDef *hUart, LIN_SlaveClassTypeDef *pLinSlave, uint8_t *pTxBuf, uint8_t TxCnt)
{
    if (TxCnt > 0)
    {
        HAL_UART_DISABLE_IT(hUart, UART_IT_SOURCE_RC);                // 发送数据前关闭接收完成中断，否则自己发送到总线上的数据会被自己收到
        HAL_UART_ENABLE_IT(hUart, UART_IT_SOURCE_TC);
        pLinSlave->State = LIN_STATE_TXDATA;

        pLinSlave->TxTotalLen = TxCnt;
        pLinSlave->pTxBuff = pTxBuf;
        hUart->Instance->TDR = pTxBuf[pLinSlave->Count++];            // 先发送1字节数据用以触发TC
        pLinSlave->State++;
    }
}


/*******************************************************************************
 * @brief  LIN 处理接收到的PID
 * @param  UARTx        : UART名称
 *                        SYM_UART1 / SYM_UART2
 * @param  pLinSlave    : Lin Slave Class handle
 * @retval None
 */
void LIN_SlaveIT_PID_Process_CallBack(UART_HandleTypeDef *hUart, LIN_SlaveClassTypeDef *pLinSlave)
{
    switch (pLinSlave->PId)
    {
        /* PID为接收8字节数据的指令 */
        case LIN_USER_PID_RXDATA:
            /* USER CODE BEGIN */
            /* USER CODE END */
            LIN_SlaveIT_PrepareRx(pLinSlave, UserRecvBuf, 8);         // 如果是接收信息的PID需要调用CtrlPrepareRx
                                                                      // 将接收8个数据，数据接收到UserRecvBuf中
            break;
        /* PID为发送8字节数据的指令 */
        case LIN_USER_PID_TXDATA:
            /* USER CODE BEGIN */
            /* USER CODE END */
            LIN_Delay_nBit(hUart, 30);                                // 等待30Bit时间用来让接收方有足够的响应时间
            LIN_SlaveIT_PrepareTx(hUart, pLinSlave, UserSendBuf, 8);  // 如果是发送信息的PID需要调用CtrlPrepareTx
                                                                      // 将UserSendBuf中8个数据发送出去
            break;
//        /* 用户自定义的PID */
//        case LIN_USER_PID_X:
//            /* USER CODE BEGIN */
//            /* USER CODE END */
//            //Code CtrlPrepareRx Or CtrlPrepareTx In This
//            break;
        /* PID无效 */
        default:
            pLinSlave->State = LIN_STATE_RXPID | LIN_ERROR_MASK;
            break;
    }
}


/*******************************************************************************
 * @brief  LIN 发送完成一帧数据回调函数
 * @param  UARTx        : UART名称
 *                        SYM_UART1 / SYM_UART2
 * @retval None
 */
void LIN_SlaveIT_SendFrameOver_CallBack(void)
{
    /* USER CODE BEGIN */
    for (uint8_t i=0; i<DATA_LEN; i++)
    {
        UserSendBuf[i] = 0;                                           // 发送完成之后将发送包清空
        UserRecvBuf[i] = 0;                                           // 发送完成之后将接收包清空
    }

    /* USER CODE END */
}


/*******************************************************************************
 * @brief  LIN 接收完一帧数据回调函数
 * @param  UARTx        : UART名称
 *                        SYM_UART1 / SYM_UART2
 * @param  pRecvBuff    : 接收到的数据存放的地址
 * @param  RecvLength   : 接收到的数据的长度
 * @retval None
 */
void LIN_SlaveIT_RecvFrameOver_CallBack(UART_HandleTypeDef *hUart, uint8_t *pRecvBuff, uint8_t RecvLength)
{
    /* USER CODE BEGIN */
    for (uint8_t i=0; i<RecvLength; i++)
    {
        UserSendBuf[i] = UserRecvBuf[i];                              // 将接收到的数据放到发送的数据包里
    }

    /* USER CODE END */
}


/*******************************************************************************
 * @brief  LIN 中断回调函数
 * @param  UARTx        : UART名称
 *                        SYM_UART1 / SYM_UART2
 * @param  pLinSlave    : Lin Slave Class handle
 * @note
 */
void LIN_SlaveIT_IRQHandlerCallBack(UART_HandleTypeDef *hUart, LIN_SlaveClassTypeDef *pLinSlave)
{
    uint8_t  RecvTmp = 0;
    uint32_t TmpBrri;
//    uint32_t LinIrq = HAL_UART_GET_IT_SOURCE(UARTx, UART_IT_SOURCE_ALL) &
//                      HAL_UART_GET_FLAG(UARTx, UART_IT_FLAG_ALL) &
//                      LIN_IRQ_ISR_MASK;

    uint32_t LinIrq = HAL_UART_GET_FLAG(hUart, LIN_IRQ_ISR_MASK);
    LinIrq &= hUart->Instance->IER;

    HAL_UART_CLR_FLAG(hUart, UART_IT_FLAG_ALL);

    // 接收间隔段 ****************************************************
    if ((LinIrq & UART_IT_FLAG_RXBRK) == UART_IT_FLAG_RXBRK)
    {
        // 准备接收同步段
        REGBITS_CLR(hUart->Instance->CR2, UARTx_CR2_TIMCR_Msk);       // 停止定时器
        hUart->Instance->TIMARR = 0x00FFFFFF;                         // 配置定时器重载值
        REGBITS_SET(hUart->Instance->CR2, UART_TIMCR_AUTOBAUD2);      // 开启自动波特率侦测2

        HAL_UART_ENABLE_IT(hUart, UART_IT_SOURCE_RXBRK | UART_IT_SOURCE_BAUD); // 任何情况只要Break发生就只保留RXBRK中断并打开BAUD中断
        pLinSlave->State = LIN_STATE_SYNC;
        return;
    }

    // 接收同步段 ****************************************************
    if ((LinIrq & UART_IT_FLAG_BAUD) == UART_IT_FLAG_BAUD)
    {
        HAL_UART_DISABLE_IT(hUart, UART_IT_SOURCE_BAUD);              // 关闭波特率检测

        // 接收到同步段计算并配置波特率
        TmpBrri = hUart->Instance->TIMCNT >> 7;
        hUart->Instance->BRRI = TmpBrri;                              // 更改波特率
        hUart->Instance->BRRF = (hUart->Instance->TIMCNT - TmpBrri) >> 3;

        HAL_UART_ENABLE_IT(hUart, UART_IT_SOURCE_RC);                 // 打开接收完成中断
        pLinSlave->State = LIN_STATE_RXPID;
        return;
    }

    // 接收数据  ****************************************************
    if ((LinIrq & UART_IT_FLAG_RC) == UART_IT_FLAG_RC)
    {
        RecvTmp = hUart->Instance->RDR;

        if ((pLinSlave->State & LIN_STATE_MASK) == LIN_STATE_RXPID)   // 接收到PID
        {
            pLinSlave->PId = RecvTmp;
            pLinSlave->Count = 0;
            LIN_SlaveIT_PID_Process_CallBack(hUart, pLinSlave);       // 用户处理PID事务的地方
        }
        else if ((pLinSlave->State & LIN_STATE_MASK) == LIN_STATE_RXDATA) // 需要接收数据阶段
        {
            pLinSlave->pRxBuff[pLinSlave->Count++] = RecvTmp;
            pLinSlave->State++;
            if (pLinSlave->Count >= pLinSlave->RxTotalLen)            // 数据接收完成
            {
                pLinSlave->State = LIN_STATE_RXSUM;
            }
        }
        else if ((pLinSlave->State & LIN_STATE_MASK) == LIN_STATE_RXSUM) // 接收校验和阶段
        {
            HAL_UART_DISABLE_IT(hUart, UART_IT_SOURCE_RC);
            if ((LIN_CalcCheckSum(pLinSlave->pRxBuff, pLinSlave->RxTotalLen) + RecvTmp) != 0xFF) // 进行校验和计算
            {
                pLinSlave->State = LIN_STATE_RXSUM | LIN_ERROR_MASK;
            }
            else
            {
                pLinSlave->State = LIN_STATE_RXOVER;
                LIN_SlaveIT_RecvFrameOver_CallBack(hUart, pLinSlave->pRxBuff, pLinSlave->RxTotalLen);
            }
        }
        return;
    }

    // 发送7字节数据和校验和  ****************************************
    if ((LinIrq & UART_IT_FLAG_TC) == UART_IT_FLAG_TC)
    {
        if ((pLinSlave->State & LIN_STATE_MASK) == LIN_STATE_TXDATA)  // 发送数据
        {
            hUart->Instance->TDR = pLinSlave->pTxBuff[pLinSlave->Count++];
            pLinSlave->State++;
            if (pLinSlave->Count >= pLinSlave->TxTotalLen)
            {
                pLinSlave->State = LIN_STATE_TXSUM;
            }
        }
        else if ((pLinSlave->State & LIN_STATE_MASK) == LIN_STATE_TXSUM) // 发送校验和
        {
            hUart->Instance->TDR = (~LIN_CalcCheckSum(pLinSlave->pTxBuff, pLinSlave->TxTotalLen)) & 0xFF;
            pLinSlave->State = LIN_STATE_TXSUMOVER;
        }
        else if ((pLinSlave->State & LIN_STATE_MASK) == LIN_STATE_TXSUMOVER) // 发送校验和完成
        {
            HAL_UART_DISABLE_IT(hUart, UART_IT_SOURCE_TC);
            pLinSlave->State = LIN_STATE_TXOVER;
            LIN_SlaveIT_SendFrameOver_CallBack();
        }
        return;
    }
}


/************************ (C) COPYRIGHT SIYIMicro *****END OF FILE*************/
